package nacos

import (
	"fmt"
	"gitee.com/ymofen/gobase"
	"gitee.com/ymofen/golang/strmap"
	"github.com/nacos-group/nacos-sdk-go/v2/clients"
	"github.com/nacos-group/nacos-sdk-go/v2/clients/config_client"
	"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
	"github.com/nacos-group/nacos-sdk-go/v2/vo"
	"runtime"
	"strings"
	"sync/atomic"
	"time"
)

type NacosConfigClient struct {
	s   string
	clt config_client.IConfigClient
}

var (
	NacosConfigClientAliveN atomic.Int32
	NacosClientAliveN       atomic.Int32
)

// servers=58.49.94.131:18351:18352
func NewNacosConfigClient(conf string) (*NacosConfigClient, error) {
	confMap := strmap.NewStrMap()
	confMap.URLFormDecode(conf)

	servers := strings.Split(confMap["servers"], ",")

	// Nacos服务端配置
	var serverConfigs []constant.ServerConfig

	//
	for _, s := range servers {
		strs := strings.Split(s, ":")
		if len(strs) < 2 {
			continue
		}
		ipAddr := strs[0]
		if len(ipAddr) == 0 {
			continue
		}

		httpPort := gobase.StrToIntDef(strs[1], 0)
		grpcPort := 0
		if len(strs) >= 3 {
			grpcPort = gobase.StrToIntDef(strs[2], 0)
		}
		if httpPort > 0 || grpcPort > 0 {
			serverConfigs = append(serverConfigs, constant.ServerConfig{IpAddr: ipAddr, Port: uint64(httpPort), GrpcPort: uint64(grpcPort)})
		}
	}
	if len(serverConfigs) == 0 {
		return nil, fmt.Errorf("nacos config server is empty")
	}

	namespaceID := confMap["namespace"]
	timeout := gobase.ParseDurationEx("timeout", time.Second*5)
	logDir := confMap["logDir"]
	cacheDir := confMap["cacheDir"]
	logLvl := confMap["logLvl"]
	if len(logLvl) == 0 {
		logLvl = "info"
	}

	var appLabels map[string]string
	confMap.RangePrefix("labels.", func(k, v string) bool {
		if appLabels == nil {
			appLabels = make(map[string]string)
		}
		appLabels[k] = v
		return true
	})

	// 客户端配置
	clientConfig := constant.ClientConfig{
		NamespaceId:   namespaceID,                    // 命名空间ID
		TimeoutMs:     uint64(timeout.Milliseconds()), // 请求超时时间
		LogDir:        logDir,                         // 日志目录
		CacheDir:      cacheDir,                       // 缓存目录
		LogLevel:      logLvl,                         // 日志级别
		Username:      confMap["username"],
		Password:      confMap["password"],
		AppConnLabels: appLabels,
	}

	// 创建配置客户端
	NacosClientAliveN.Add(1)
	client, err := clients.NewConfigClient(
		vo.NacosClientParam{
			ClientConfig:  &clientConfig,
			ServerConfigs: serverConfigs,
		},
	)
	runtime.SetFinalizer(client, func(obj interface{}) {
		NacosClientAliveN.Add(-1)
	})

	rval := &NacosConfigClient{s: conf, clt: client}
	NacosConfigClientAliveN.Add(1)
	runtime.SetFinalizer(rval, func(obj interface{}) {
		NacosConfigClientAliveN.Add(-1)
	})

	return rval, err

}

func (this *NacosConfigClient) GetConfig(dataID, group string) (string, error) {
	content, err := this.clt.GetConfig(vo.ConfigParam{
		DataId: dataID,
		Group:  group,
	})
	if err != nil {
		return "", err
	}

	return content, nil

}

func (this *NacosConfigClient) Close() error {
	clt := this.clt
	if clt != nil {
		clt.CloseClient()
		this.clt = nil
	}
	return nil
}

func (this *NacosConfigClient) ListenConfig(dataId, groupId string, fn func(namespace, group, dataId, data string)) (handle interface{}, err error) {
	err = this.clt.ListenConfig(vo.ConfigParam{
		DataId:   dataId,
		Group:    groupId,
		OnChange: fn,
	})
	if err != nil {
		return nil, err
	}
	return [3]interface{}{dataId, groupId, fn}, nil
}

func (this *NacosConfigClient) CancelListenConfig(handle interface{}) error {
	if handle == nil {
		return fmt.Errorf("handle is nil")
	}
	vals, ok := handle.([3]interface{})
	if !ok {
		return fmt.Errorf("handle is invalid")
	}

	return this.clt.CancelListenConfig(vo.ConfigParam{
		DataId:   vals[0].(string),
		Group:    vals[1].(string),
		OnChange: vals[2].(func(namespace, group, dataId, data string)),
	})
}

func CreateNacosConfigClientForFactory(args ...interface{}) (interface{}, error) {
	clt, err := NewNacosConfigClient(args[0].(string))
	if err != nil {
		return nil, err
	}
	return clt, err
}

func init() {
	gobase.RegisterFactoryCreateFunc("nacosCfgClient", CreateNacosConfigClientForFactory)
}
